// 接口，约定一个对象需要包含且只能包含哪些成员以及这些成员分别是什么类型
interface Message {
    from: string
    to: string
    content: string
}
const v_message: Message = {
    from: 'Bob',
    to: 'Amy',
    content: 'TypeScript is very delicious'
}


// 泛型，约定一个类型包含一个或多个成员，但是这些成员的类型由调用方确定
interface HttpResponse<T> {
    code: number
    msg: string
    data: T
}
// data是字符串数组
const v_string_array_response: HttpResponse<string[]> = {
    code: 0,
    msg: 'ok',
    data: ['a', 'b', 'c']
}
// data是boolean
const v_boolean_respone: HttpResponse<boolean> = {
    code: 0,
    msg: 'ok',
    data: false
}
// data是一个Message类型的复杂对象
const v_message_response: HttpResponse<Message> = {
    code: 0,
    msg: 'ok',
    data: {
        from: 'Bob',
        to: 'Amy',
        content: 'TypeScript is very delicious'
    }
}


// 抽象类，一个约定了成员的类，但其中某些行为如何实现需要由调用方确定
abstract class Programer {
    // 程序员姓名初始化
    constructor(protected name: string) {
    }
    // 所有程序员通用的自我介绍
    resume() {
        console.log('I`m ${this.name}, I·m single, thx!')
    }
    // 让程序员写一句helloworld试试水平
    abstract helloWorld(): void;
}
class JavaScriptProgramer extends Programer {
    constructor(name: string) {
        super(name)
    }
    helloWorld(): void {
        console.log(`
            // 高端的代码，往往只需要一句就能显示水平
            console.log('hello world!')
        `)
    }
}
class JavaProgramer extends Programer {
    constructor(name: string) {
        super(name)
    }
    helloWorld(): void {
        console.log(`
            public class Program{
                public static int main(string[] args){
                    // todo:不要忘记打分号
                    system.println('hello world');
                }
            }
        `)
    }
}
// 消除不同类型程序员之间的差异，使用统一的API实现相同逻辑
[
    new JavaScriptProgramer('顺聪'),
    new JavaProgramer('一位不愿意透露姓名的Java大牛')
].forEach(programer => {
    // 先来一段自我介绍
    programer.resume()
    // 表演真正的技术
    programer.helloWorld()
})

// 方法重载，同名的方法，具有多种传参方式
// 一、实现jQuery的$.css()方法，强迫症类名必须要大驼峰命名
class JQuery {
    private el: HTMLElement
    constructor(private selector: string) {
        // !表示确定这个值一定存在
        this.el = document.querySelector<HTMLElement>(selector)!
    }
    // 不论何种情况，我都要使用属性名和属性值改变元素css，因此我将这个重复的逻辑封装为一个方法，方便复用
    // 但是这个方法是内部使用的，因此我加了private修饰符（而不是使用_setCSS()这种通过名称约定的方式私有方法），让外面无法直接调用此方法
    private setCSS(property: string, value: string | number) {
        // 我十分确定this.el.style一定可以使用索引访问并修改其值，所以我使用 @ts-ignore注释让TS不对下面这行代码进行检测
        // @ts-ignore 
        this.el.style[property] = value
    }
    // 下面连续的分号结尾的这两行是给TS看的
    css(property: string, value: string | number): JQuery;
    css(propertyDescriptors: object): JQuery;
    css(): JQuery {
        const [first, second] = Array.from(arguments)
        if (typeof first === 'object') {
            Object.keys(first).forEach(key => {
                this.setCSS(key, first[key])
            })
        } else {
            this.setCSS(first, second)
        }
        // 我要支持链式调用，因此返回当前类实例
        return this
    }
}
const $ = (selector: string) => new JQuery(selector)
const $el = $('#app')
// 链式调用，支持字符串值/支持数字值/支持对象
$el.css('backgroundColor', 'red')
    //     
    .css('zIndex', 1000)
    // 
    .css({
        display: 'flex',
        justifyContent: 'space-between',
        alignItems: 'center',
        width: '100%'
    })

// 二、实现react hooks的useState
type DispatchStateAction<T> = (newValue: T | ((currentValue: T) => T)) => void

// 给TS看的
function useState<T>(initialValue: T | (() => T)): [T, DispatchStateAction<T>]
function useState<T = undefined>(): [T | undefined, DispatchStateAction<T | undefined>]
// 函数实现
function useState<T = undefined>(...args: any[]): [T | undefined, DispatchStateAction<T | undefined>] {
    const [initialValue] = args
    let value = typeof initialValue === 'function' ? initialValue() : initialValue
    return [value as T, (newValue) => {
        // fiber的实现细节此处不做拓展
        console.log(`useState's value was changed by dispatch function!`, newValue)
    }]
}
// 使用
const [value, setValue] = useState();
const [value2, setValue2] = useState(() => false)
const [value3, setValue3] = useState<number>()
const [value4, setValue4] = useState<number>(() => 1000)

setValue4(2000)
setValue4((val) => val + 2000)